/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.intel.factorytest.activity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

public class PointerLocationViewRect_bak extends View {
    private static final String TAG = "Pointer";
    private static final int RECTLEN = 50;
//    private final int remainderV;
//    private final int remainderH;
    private boolean needDeviate = false;
    private final float deviationH;
    private final float deviationV;
    private int rectCountH = 0;
    private int rectCountV = 0;
    
    private Context ctx;
    
    // screen width
    private int widthScr = 0;
    // screen height
    private int heightScr = 0;
    
    private PointerLocation activity;
    private int orientation = Configuration.ORIENTATION_PORTRAIT;
    
    private boolean isFirstDrawn = true;
    
    public static class PointerState {
        // Trace of previous points.
        private float[] mTraceX = new float[32];
        private float[] mTraceY = new float[32];
        private int mTraceCount;
        
        // True if the pointer is down.
        private boolean mCurDown;
        
        // Most recent coordinates.
        private MotionEvent.PointerCoords mCoords = new MotionEvent.PointerCoords();
        
        // Most recent velocity.
        private float mXVelocity;
        private float mYVelocity;
        
        public void clearTrace() {
            mTraceCount = 0;
        }
        
        public void addTrace(float x, float y) {
            int traceCapacity = mTraceX.length;
            if (mTraceCount == traceCapacity) {
                traceCapacity *= 2;
                float[] newTraceX = new float[traceCapacity];
                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
                mTraceX = newTraceX;
                
                float[] newTraceY = new float[traceCapacity];
                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
                mTraceY = newTraceY;
            }
            
            mTraceX[mTraceCount] = x;
            mTraceY[mTraceCount] = y;
            mTraceCount += 1;
        }
    }

    private final Paint mBlackPaint;
    private final Paint mRedPaint;
    private final Paint mPathPaint;
    private boolean mCurDown;
    private int mActivePointerId;
    private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
    public ArrayList<PointerState> getmPointers() {
        return mPointers;
    }
    
    private final FasterStringBuilderIntelMAT mText = new FasterStringBuilderIntelMAT();
    
    private boolean mPrintCoords = true;
    
    public PointerLocationViewRect_bak(Context ctx) {
        this(ctx, null);  
    }
    
    public PointerLocationViewRect_bak(Context context, AttributeSet attrs){  
        this(context, attrs, 0);  
    }  
    
    public PointerLocationViewRect_bak(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);  
            this.ctx = context;
            activity = (PointerLocation) this.ctx;
            widthScr = activity.widthScreen;
            heightScr = activity.heightScreen;
            
            deviationH = widthScr % RECTLEN;
            deviationV = heightScr % RECTLEN;
//            widthScr = widthScr - remainderH;
//            heightScr = heightScr - remainderV;
            
            orientation = activity.getResources().getConfiguration().orientation;
            
            setFocusable(true);
            
            mBlackPaint = new Paint();
            mBlackPaint.setAntiAlias(true);
            mBlackPaint.setARGB(255, 0, 0, 0);
            mBlackPaint.setStyle(Paint.Style.STROKE);
            mBlackPaint.setStrokeWidth(2);
            
            mRedPaint = new Paint();
            mRedPaint.setAntiAlias(true);
            mRedPaint.setARGB(255, 255, 80, 80);
            mRedPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            mRedPaint.setStrokeWidth(2);
            
            mPathPaint = new Paint();
            mPathPaint.setAntiAlias(false);
            mPathPaint.setARGB(255, 20, 255, 20);
            mPathPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            mPathPaint.setStrokeWidth(2);
            
            PointerState ps = new PointerState();
            mPointers.add(ps);
            mActivePointerId = 0;
            
            logInputDeviceCapabilities();
    }     
    
    private void logInputDeviceCapabilities() {
        int[] deviceIds = InputDevice.getDeviceIds();
        for (int i = 0; i < deviceIds.length; i++) {
            InputDevice device = InputDevice.getDevice(deviceIds[i]);
            if (device != null) {
                Log.i(TAG, device.toString());
            }
        }
    }

    public void setPrintCoords(boolean state) {
        mPrintCoords = state;
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {

        synchronized (mPointers) {
            initDrawn(canvas);
            final int NP = mPointers.size();
            // Pointer trace.
            for (int p = 0; p < NP; p++) {
                final PointerState ps = mPointers.get(p);

                // Draw path.
                final int N = ps.mTraceCount;
                float lastX = 0, lastY = 0;
                boolean haveLast = false;
                boolean drawn = false;
                for (int i = 0; i < N; i++) {
                    float x = ps.mTraceX[i];
                    float y = ps.mTraceY[i];
                    if (Float.isNaN(x)) {
                        haveLast = false;
                        continue;
                    }
                    if (haveLast) {
                        Rect rect = whichRect(x, y);
                        canvas.drawRect(rect, mPathPaint);
                        drawn = true;
                    }
                    lastX = x;
                    lastY = y;
                    haveLast = true;
                }
            }
        }

    }
    
    
    private void initDrawn(Canvas canvas) {
        rectCountH = widthScr / RECTLEN;
        rectCountV = heightScr / RECTLEN;
        rectCountH = (widthScr % RECTLEN == 0) ? rectCountH : (rectCountH + 1);
        rectCountV = (heightScr % RECTLEN != 0 && heightScr % RECTLEN <= 3) ? rectCountV : (rectCountV + 1);
        
        int midCoordsY = heightScr / 2 - heightScr / 2 % RECTLEN;
        for (int i = 0; i < rectCountH; i++) {
            
            if (i == (rectCountH / 2 + 1)) {
                if ((int)deviationH != 0) {
                    drawRect(canvas, i * RECTLEN, 0, (i + 1) * RECTLEN + deviationH, RECTLEN, mBlackPaint, deviationH, false);
                    drawRect(canvas, i * RECTLEN, midCoordsY, (i + 1) * RECTLEN + deviationH, midCoordsY + RECTLEN, mBlackPaint, deviationH, false);
                    drawRect(canvas, i * RECTLEN, heightScr - RECTLEN, (i + 1) * RECTLEN + deviationH, heightScr, mBlackPaint, deviationH, false);
                    
                    drawRect(canvas, i * RECTLEN, 0, (i + 1) * RECTLEN + deviationH, RECTLEN, mRedPaint, deviationH, false);
                    drawRect(canvas, i * RECTLEN, midCoordsY, (i + 1) * RECTLEN + deviationH, midCoordsY + RECTLEN, mRedPaint, deviationH, false);
                    drawRect(canvas, i * RECTLEN, heightScr - RECTLEN, (i + 1) * RECTLEN + deviationH, heightScr, mRedPaint, deviationH, false);
                    
                    needDeviate = true;
                    continue;
                }
            }
            
            drawRect(canvas, i * RECTLEN, 0, (i + 1) * RECTLEN, RECTLEN, mBlackPaint, deviationH, false);
            drawRect(canvas, i * RECTLEN, midCoordsY, (i + 1) * RECTLEN, midCoordsY + RECTLEN, mBlackPaint, deviationH, false);
            drawRect(canvas, i * RECTLEN, heightScr - RECTLEN, (i + 1) * RECTLEN, heightScr, mBlackPaint, deviationH, false);
            
            drawRect(canvas, i * RECTLEN, 0, (i + 1) * RECTLEN, RECTLEN, mRedPaint, deviationH, false);
            drawRect(canvas, i * RECTLEN, midCoordsY, (i + 1) * RECTLEN, midCoordsY + RECTLEN, mRedPaint, deviationH, false);
            drawRect(canvas, i * RECTLEN, heightScr - RECTLEN, (i + 1) * RECTLEN, heightScr, mRedPaint, deviationH, false);
        }
        needDeviate = false;
        
        int midCoordsX = widthScr / 2 - widthScr / 2 % RECTLEN;
        for (int i = 0; i < rectCountV; i++) {

            if (i == (rectCountV / 2 + 1)) {
                if ((int)deviationV != 0) {
                    drawRect(canvas, 0, i * RECTLEN, RECTLEN, i * RECTLEN + deviationV, mBlackPaint, deviationV, true);
                    drawRect(canvas, midCoordsX, i * RECTLEN, midCoordsX + RECTLEN, i * RECTLEN + deviationV, mBlackPaint, deviationV, true);
                    drawRect(canvas, widthScr - RECTLEN, i * RECTLEN, widthScr, i * RECTLEN + deviationV, mBlackPaint, deviationV, true);
                    
                    drawRect(canvas, 0, i * RECTLEN, RECTLEN, i * RECTLEN + deviationV, mRedPaint, deviationV, true);
                    drawRect(canvas, midCoordsX, i * RECTLEN, midCoordsX + RECTLEN, i * RECTLEN + deviationV, mRedPaint, deviationV, true);
                    drawRect(canvas, widthScr - RECTLEN, i * RECTLEN, widthScr, i * RECTLEN + deviationV, mRedPaint, deviationV, true);
                    needDeviate = true;
                    continue;
                }
            }

            drawRect(canvas, 0, i * RECTLEN, RECTLEN, (i + 1) * RECTLEN, mBlackPaint, deviationV, true);
            drawRect(canvas, midCoordsX, i * RECTLEN, midCoordsX + RECTLEN, (i + 1) * RECTLEN, mBlackPaint, deviationV, true);
            drawRect(canvas, widthScr - RECTLEN, i * RECTLEN, widthScr, (i + 1) * RECTLEN, mBlackPaint, deviationV, true);

            drawRect(canvas, 0, i * RECTLEN, RECTLEN, (i + 1) * RECTLEN, mRedPaint, deviationV, true);
            drawRect(canvas, midCoordsX, i * RECTLEN, midCoordsX + RECTLEN, (i + 1) * RECTLEN, mRedPaint, deviationV, true);
            drawRect(canvas, widthScr - RECTLEN, i * RECTLEN, widthScr, (i + 1) * RECTLEN, mRedPaint, deviationV, true);
        }
        needDeviate = false;
    }
    
    private void drawRect(Canvas canvas, float left, float top, float right, float bottom, Paint paint, float deviation, boolean isVertical) {
        if (needDeviate) {
            if (isVertical) {
                top = top + deviation - RECTLEN;
                bottom = bottom + deviation - RECTLEN;
            } else {
                left = left + deviation - RECTLEN;
                right = right + deviation - RECTLEN;
            }
        }
        System.out.println("rect coords: left=" + left + ",top=" + top + ",right=" + right + ",bottom=" + bottom);
        canvas.drawRect(left, top, right, bottom, paint);
    }
    
    private Set<Map<Integer, Integer>> set = new HashSet<Map<Integer, Integer>>();
    
    private Rect whichRect(float coordsX, float coordsY) {
        Rect target = null;
        
        int NX = (int) coordsX / RECTLEN;
        int NY = (int) coordsY / RECTLEN;
        
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            if (Math.abs(heightScr - coordsY) < RECTLEN) {
                target = new Rect(NX * RECTLEN, heightScr - RECTLEN, (NX + 1) * RECTLEN, heightScr);
                map.put(NX * RECTLEN, heightScr - RECTLEN);
                set.add(map);
            }
        } else {
            if (Math.abs(widthScr - coordsX) < RECTLEN) {
                target = new Rect(widthScr - RECTLEN, NY * RECTLEN, widthScr, (NY + 1) * RECTLEN);
                map.put(widthScr - RECTLEN, NY * RECTLEN);
                set.add(map);
            }
        }
        
        if (target == null) {
            target = new Rect(NX * RECTLEN, NY * RECTLEN, (NX + 1) * RECTLEN, (NY + 1) * RECTLEN);
            map.put(NX * RECTLEN, NY * RECTLEN);
            set.add(map);
        }
        
        int rectTotal = (rectCountH + rectCountV) * 3 - 9;
        System.out.println("tangjun rect total=" + rectTotal + ",set size=" + set.size());
        System.out.println("tangjun rectCountH=" + rectCountH + "rectCountV=" + rectCountV);
        if (set.size() == rectTotal) {
            activity.setIs_pass(true);
            activity.setTestResult();
            activity.finish();
        }
        return target;
    }
    
    private void logPointerCoords(MotionEvent.PointerCoords coords, int id) {
        Log.i(TAG, mText.clear()
                .append("Pointer ").append(id + 1)
                .append(": (").append(coords.x, 3).append(", ").append(coords.y, 3)
                .append(") Pressure=").append(coords.pressure, 3)
                .append(" Size=").append(coords.size, 3)
                .append(" TouchMajor=").append(coords.touchMajor, 3)
                .append(" TouchMinor=").append(coords.touchMinor, 3)
                .append(" ToolMajor=").append(coords.toolMajor, 3)
                .append(" ToolMinor=").append(coords.toolMinor, 3)
                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
                .append("deg").toString());
    }

    public void addTouchEvent(MotionEvent event) {
        synchronized (mPointers) {
            int action = event.getAction();
            
            int NP = mPointers.size();
            
            if (action == MotionEvent.ACTION_DOWN
                    || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
                final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
                
                final int id = event.getPointerId(index);
                while (NP <= id) {
                    PointerState ps = new PointerState();
                    mPointers.add(ps);
                    NP++;
                }
                
                if (mActivePointerId < 0 ||
                        ! mPointers.get(mActivePointerId).mCurDown) {
                    mActivePointerId = id;
                }
                
                final PointerState ps = mPointers.get(id);
                ps.mCurDown = true;
                if (mPrintCoords) {
                    Log.i(TAG, mText.clear().append("Pointer ")
                            .append(id + 1).append(": DOWN").toString());
                }
            }
            
            final int NI = event.getPointerCount();
            
            mCurDown = action != MotionEvent.ACTION_UP
                    && action != MotionEvent.ACTION_CANCEL;

            
            for (int i=0; i<NI; i++) {
                final int id = event.getPointerId(i);
                final PointerState ps = mPointers.get(id);
                final int N = event.getHistorySize();
                for (int j=0; j<N; j++) {
                    event.getHistoricalPointerCoords(i, j, ps.mCoords);
                    if (mPrintCoords) {
                        logPointerCoords(ps.mCoords, id);
                    }
                    if (isValidArea(event.getHistoricalX(i, j), event.getHistoricalY(i, j))){
                        ps.addTrace(event.getHistoricalX(i, j), event.getHistoricalY(i, j));
                    }
                }
                event.getPointerCoords(i, ps.mCoords);
                if (mPrintCoords) {
                    logPointerCoords(ps.mCoords, id);
                }
                if (isValidArea(ps.mCoords.x, ps.mCoords.y)){
                    ps.addTrace(ps.mCoords.x, ps.mCoords.y);
                }
            }
            
            if (action == MotionEvent.ACTION_UP
                    || action == MotionEvent.ACTION_CANCEL
                    || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
                final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
                
                final int id = event.getPointerId(index);
                final PointerState ps = mPointers.get(id);
                ps.mCurDown = false;
                if (mPrintCoords) {
                    Log.i(TAG, mText.clear().append("Pointer ")
                            .append(id + 1).append(": UP").toString());
                }
                
                if (action == MotionEvent.ACTION_UP
                        || action == MotionEvent.ACTION_CANCEL) {
                    mCurDown = false;
                } else {
                    if (mActivePointerId == id) {
                        mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
                    }
                    ps.addTrace(Float.NaN, Float.NaN);
                }
            }
            
            postInvalidate();
        }
    }
    
    private boolean isValidArea(float coordX, float coordY) {
        boolean shown = true;
        int midCoordsX = widthScr / 2 - widthScr / 2 % RECTLEN;
        int midCoordsY = heightScr / 2 - heightScr / 2 % RECTLEN;
        
        System.out.println("tangjun midC");
        
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            if (coordX > RECTLEN && coordX < widthScr - RECTLEN && coordY > RECTLEN && coordY < heightScr - RECTLEN) {
                shown = false;
            }
            if (coordX > midCoordsX && coordX < midCoordsX + RECTLEN || coordY > midCoordsY && coordY < midCoordsY + RECTLEN) {
                shown = true;
            }
        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            if (coordX > RECTLEN && coordX < widthScr - RECTLEN && coordY > RECTLEN && coordY < heightScr - RECTLEN || coordY > heightScr) {
                shown = false;
            }
            
            if (coordY <= heightScr) {
                if (coordX > midCoordsX && coordX < midCoordsX + RECTLEN || coordY > midCoordsY && coordY < midCoordsY + RECTLEN) {
                    shown = true;
                }
            }
        }
        System.out.println("isValidArea: (x,y)-->(" + coordX + "," + coordY + ") shown=" + shown);
        
        return shown;
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
//        activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        addTouchEvent(event);
        return true;
    }

    @Override
    public boolean onTrackballEvent(MotionEvent event) {
        Log.i(TAG, "Trackball: " + event);
        return super.onTrackballEvent(event);
    }
    
    private static final class FasterStringBuilderIntelMAT {
        private char[] intel_mat_mChars;
        private int intel_mat_mLength;
        
        public FasterStringBuilderIntelMAT() {
            intel_mat_mChars = new char[64];
        }
        
        public FasterStringBuilderIntelMAT clear() {
            intel_mat_mLength = 0;
            return this;
        }
        
        public FasterStringBuilderIntelMAT append(String value) {
            final int valueLength = value.length();
            final int index = reserve(valueLength);
            value.getChars(0, valueLength, intel_mat_mChars, index);
            intel_mat_mLength += valueLength;
            return this;
        }
        
        public FasterStringBuilderIntelMAT append(int value) {
            return append(value, 0);
        }
        
        public FasterStringBuilderIntelMAT append(int value, int zeroPadWidth) {
            final boolean negative = value < 0;
            if (negative) {
                value = - value;
                if (value < 0) {
                    append("-2147483648");
                    return this;
                }
            }
            
            int index = reserve(11);
            final char[] chars = intel_mat_mChars;
            
            if (value == 0) {
                chars[index++] = '0';
                intel_mat_mLength += 1;
                return this;
            }
            
            if (negative) {
                chars[index++] = '-';
            }

            int divisor = 1000000000;
            int numberWidth = 10;
            while (value < divisor) {
                divisor /= 10;
                numberWidth -= 1;
                if (numberWidth < zeroPadWidth) {
                    chars[index++] = '0';
                }
            }
            
            do {
                int digit = value / divisor;
                value -= digit * divisor;
                divisor /= 10;
                chars[index++] = (char) (digit + '0');
            } while (divisor != 0);
            
            intel_mat_mLength = index;
            return this;
        }
        
        public FasterStringBuilderIntelMAT append(float value, int precision) {
            int scale = 1;
            for (int i = 0; i < precision; i++) {
                scale *= 10;
            }
            value = (float) (Math.rint(value * scale) / scale);
            
            append((int) value);

            if (precision != 0) {
                append(".");
                value = Math.abs(value);
                value -= Math.floor(value);
                append((int) (value * scale), precision);
            }
            
            return this;
        }
        
        @Override
        public String toString() {
            return new String(intel_mat_mChars, 0, intel_mat_mLength);
        }
        
        private int reserve(int length) {
            final int oldLength = intel_mat_mLength;
            final int newLength = intel_mat_mLength + length;
            final char[] oldChars = intel_mat_mChars;
            final int oldCapacity = oldChars.length;
            if (newLength > oldCapacity) {
                final int newCapacity = oldCapacity * 2;
                final char[] newChars = new char[newCapacity];
                System.arraycopy(oldChars, 0, newChars, 0, oldLength);
                intel_mat_mChars = newChars;
            }
            return oldLength;
        }
    }
}
